package cn.fan.service;

import cn.fan.constants.DataBaseConstants;
import cn.fan.mapper.FundDataMapper;
import cn.fan.pojo.FundData;
import cn.fan.pojo.FundMain;
import cn.fan.pojo.rxiong.RXFundData;
import cn.fan.pojo.simulate.FundMoveAvg;
import cn.fan.pojo.simulate.Trade;
import cn.fan.redis.FundDataRedis;
import cn.fan.redis.FundMainRedis;
import cn.fan.utils.Common;
import cn.fan.utils.UserUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class FundDataService {
    // 更新（添加、删除、获取）失败
    private static final int FAIL = -1;

    private static final Long AUTO = 0L;

    @Autowired
    private FundDataRedis fundDataRedis;

    @Autowired
    private FundMainRedis fundMainRedis;

    @Autowired
    private FundDataMapper fundDataMapper;

    @Autowired
    private RxiongService rxiongService;

    public FundData getById(long id){
        return fundDataRedis.getById(id);
    }

    public FundData getByCodeDate(String code,String fundDate){
//        FundData fundData=new FundData();
//        fundData.setCode(code);
//        fundData.setFunddate(fundDate);
//
//        Map<String,Object> map=new HashMap<>();
//        map.put("fundData",fundData);
//        List<FundData> list = list(map);
//
//        return list.size()==0 ? null : list.get(0);
        return fundDataMapper.findByCodeDate(code,fundDate);
    }

    public int add(FundData fundData){
        long sysUserId = UserUtils.getSysUserId();
        if(sysUserId == FAIL)
            return FAIL;

        return addFundData(fundData, sysUserId);
    }

    private int addFundData(FundData fundData, long sysUserId) {
        fundData.setCreateDate(new Date());
        fundData.setCreateBy(sysUserId);

        int i = fundDataMapper.insert(fundData);
        fundDataRedis.putById(fundData.getId(),fundData);
        return i;
    }

    public int delete(long id){
        int i = fundDataMapper.deleteById(id);
        fundDataRedis.delete(id);
        return i;
    }

    public int edit(FundData fundData){
        FundData fundData1 = fundDataRedis.getById(fundData.getId());
        if (fundData1==null)
            return DataBaseConstants.NO_DATA;

        if(fundData.getNetworth() != null)
            fundData1.setNetworth(fundData.getNetworth());
        if (fundData.getTotalnetworth() != null)
            fundData1.setNetworth(fundData.getTotalnetworth());
        if (fundData.getMillioncopiesincome() != null)
            fundData1.setNetworth(fundData.getMillioncopiesincome());
        if (fundData.getSevendaysyearincome() != null)
            fundData1.setNetworth(fundData.getSevendaysyearincome());

        int i=fundDataMapper.updateById(fundData1);
        fundDataRedis.putById(fundData1.getId(),fundData1);
        return i;
    }

    // 数据库中获取
    public List<FundData> list(Map map){
        LambdaQueryWrapper<FundData> queryWrapper = getFundDataLambdaQueryWrapper(map);

        List<FundData> list =fundDataMapper.selectList(queryWrapper);
        return list;
    }

    public IPage<FundData> page(Map map, long currentPage, long pageSize){
        IPage<FundData> page = new Page<>(currentPage,pageSize);

        LambdaQueryWrapper<FundData> queryWrapper = getFundDataLambdaQueryWrapper(map);

        IPage<FundData> userIPage = fundDataMapper.selectPage(page, queryWrapper);
        return userIPage;
    }

    // redis中获取
    public List<FundData> listByRedis(Map map){
        if(null==map || map.size()==0 || null == map.get("fundData"))
            return new ArrayList<>();

        String fundDataJson = JSON.toJSONString(map.get("fundData"));
        FundData fundData = JSONObject.parseObject(fundDataJson,FundData.class);

        if(null!=fundData.getId()){
            FundData data = getById(fundData.getId());
            List<FundData> fundDataList=new ArrayList<>();
            fundDataList.add(data);
            return fundDataList;
        }

        List<FundData> fundDataList = fundDataRedis.listGetById(fundData.getCode());

        Object startDate = map.get("startDate");
        Object endDate = map.get("endDate");
        boolean isStartDate = null!=startDate && StringUtils.hasText(startDate.toString());
        boolean isEndDate = null!=endDate && StringUtils.hasText(endDate.toString());

        System.out.println("startdate:"+startDate.toString());

        List<FundData> fundDatas = new ArrayList<>();
        if( isStartDate || isEndDate ){
            for(FundData fd:fundDataList){
                if( (isStartDate && startDate.toString().compareTo(fd.getFunddate())>0 ||
                        (isEndDate && fd.getFunddate().compareTo(endDate.toString())>0) ) ){
                    continue;
                }
                fundDatas.add(fd);
            }
        }
        return fundDatas;
    }


    private LambdaQueryWrapper<FundData> getFundDataLambdaQueryWrapper(Map map) {
        LambdaQueryWrapper<FundData> queryWrapper = new LambdaQueryWrapper<FundData>();

        if(null==map || map.size()==0)
            return queryWrapper;

        if(null != map.get("fundData")){
            String fundDataJson = JSON.toJSONString(map.get("fundData"));
            FundData fundData = JSONObject.parseObject(fundDataJson,FundData.class);

            queryWrapper.eq(null!=fundData.getId(),
                    FundData::getId,
                    fundData.getId());
            queryWrapper.eq(null!=fundData.getCode(),
                    FundData::getCode,
                    fundData.getCode());
        }

        Object startDate = map.get("startDate");
        queryWrapper.ge( (null!=startDate && StringUtils.hasText(startDate.toString())) ,
                FundData::getFunddate,
                startDate);
        Object endDate = map.get("endDate");
        queryWrapper.le( (null!=endDate && StringUtils.hasText(endDate.toString())) ,
                FundData::getFunddate,
                endDate);
        queryWrapper.orderByAsc(FundData::getFunddate);
        return queryWrapper;
    }

    // 增量同步
    public void synchro(String codes) {
        Map<String,Object> map = new HashMap<>();
        map.put("codes",codes);

        String[] strings = codes.split(",");
        boolean isNull = false;
        String minDate = "";
        for (String code:strings) {
            FundData fundDataMax=fundDataMapper.getMax(code);
            if (null==fundDataMax){
                isNull = true;
                break;
            }else if( "".equals(minDate)){
                minDate=fundDataMax.getFunddate();
            }else if(minDate.compareTo(fundDataMax.getFunddate())>0){
                minDate=fundDataMax.getFunddate();
            }

        }

        if (!isNull){
            map.put("startDate",minDate);
        }

        addSynchroData(map);
    }

    // 全量同步
    public void synchroAll(String codes) {
        Map<String,Object> map = new HashMap<>();
        map.put("codes",codes);
        addSynchroData(map);
    }

    private void addSynchroData(Map<String, Object> map) {
        List<RXFundData> rxFundDataList = rxiongService.getlist(map);

        for(RXFundData rxFundData:rxFundDataList){
            String[][] netWorthData = rxFundData.getNetWorthData();
            String[][] totalNetWorthData = rxFundData.getTotalNetWorthData();
            String[][] millionCopiesIncomeData = rxFundData.getMillionCopiesIncomeData();
            String[][] sevenDaysYearIncomeData = rxFundData.getSevenDaysYearIncomeData();

            if(netWorthData==null && millionCopiesIncomeData==null) // 无数据
                return;

            Map<String,FundData> fundDataMap = new HashMap<>();
            String[][] forStr = netWorthData!=null ? netWorthData : millionCopiesIncomeData;
            boolean isEmptyNetWorthData = netWorthData!=null;

            for(int i=0;i<forStr.length;i++){
                FundData fundData=new FundData();
                fundData.setCode(rxFundData.getCode());
                fundData.setFunddate(forStr[i][0]);

                FundData byCodeDate = getByCodeDate(fundData.getCode(), fundData.getFunddate());
                if(null != byCodeDate)  // 数据库存在数据
                    continue;

                if(isEmptyNetWorthData){
                    if( netWorthData[0].length>1 && null!=netWorthData[i][1]) {
                        fundData.setNetworth(Float.parseFloat(netWorthData[i][1]));
                    }else {
                        continue;
                    }
                }else {
                    if(millionCopiesIncomeData[0].length>1 && null!=millionCopiesIncomeData[i][1]){
                        fundData.setMillioncopiesincome(Float.parseFloat(millionCopiesIncomeData[i][1]));
                    }else {
                        continue;
                    }
                }

                fundDataMap.put(fundData.getFunddate(),fundData);
            }

            for(int i=0;i<forStr.length;i++){
                FundData fundData=fundDataMap.get(forStr[i][0]);
                if (null==fundData)
                    continue;

                if(isEmptyNetWorthData){
                    if(totalNetWorthData!=null && totalNetWorthData[0].length>1 && null!=totalNetWorthData[i][1])
                        fundData.setTotalnetworth(Float.parseFloat(totalNetWorthData[i][1]));
                }else{
                    if(sevenDaysYearIncomeData!=null && sevenDaysYearIncomeData[0].length>1 && null!=sevenDaysYearIncomeData[i][1])
                        fundData.setSevendaysyearincome(Float.parseFloat(sevenDaysYearIncomeData[i][1]));
                }
            }

            for (String key:fundDataMap.keySet()){
                FundData fundData=fundDataMap.get(key);
                addFundData(fundData, AUTO);
            }
        }
    }

    // 所有基金增量同步
    public void allDataSynchro() {
        String codes=getCodes();
        synchro(codes);
    }

    // 所有基金全量同步
    public void allDataSynchroAll() {
        String codes=getCodes();
        synchroAll(codes);
    }

    private String getCodes(){
        List<FundMain> fundMains=fundMainRedis.getList();
        if (fundMains.size()==0)
            return "";

        String codes="";
        for (FundMain fundmain : fundMains) {
            codes += fundmain.getCode()+",";
        }
        codes=codes.substring(0,codes.length()-1);
        return codes;
    }

    public List<FundMoveAvg> moveAvg(Map<String, Object> map) {
        List<FundMoveAvg> fundMoveAvgList=new ArrayList<>();
        List<FundData> fundDataList=list(map);
        if(fundDataList.size()==0)
            return new ArrayList<>();

        if(fundDataList.get(0).getNetworth()==null)
            return new ArrayList<>();

        int maVal = Integer.parseInt(map.get("maVal").toString());
        float[] mas = new float[maVal];

        for(int i=0;i<fundDataList.size();i++){
            mas[i%maVal]=fundDataList.get(i).getNetworth();

            FundMoveAvg fundMoveAvg=new FundMoveAvg();
            fundMoveAvg.setFunddate(fundDataList.get(i).getFunddate());
            if(i<maVal) {
                fundMoveAvg.setMa(fundDataList.get(0).getNetworth());
            }else {
                float avg = Common.avg(mas);
                fundMoveAvg.setMa(avg);
            }
            fundMoveAvgList.add(fundMoveAvg);
        }

        return fundMoveAvgList;
    }


    public Map<String, Object> pandect(Map<String, Object> map) {
        List<FundData> fundDatas=list(map);

        if(fundDatas.size()==0)
            return new HashMap<>();
        if(fundDatas.get(0).getNetworth()==null)
            return new HashMap<>();

        Map<String,Object> pandect=new HashMap<>();

        // 收益一览
        Map<String,Object> profitGlance=new HashMap<>();
        int maVal = Integer.parseInt(map.get("maVal").toString());
        float initCash = Float.parseFloat(map.get("initCash").toString());
        float buyThreshold = Float.parseFloat(map.get("buyThreshold").toString());
        float sellThreshold = Float.parseFloat(map.get("sellThreshold").toString());
        float init = fundDatas.get(0).getNetworth();
        float cash = initCash;
        float share=0;
        float[] mals=new float[maVal];

        // 交易统计
        Map<String,Object> tradingStatistics=new HashMap<>();
        List<Trade> tradeList=new ArrayList<>();
        int winCount = 0;
        int lossCount = 0;
        float totalWinRate = 0;
        float totalLossRate = 0;

        float[] profitList=new float[fundDatas.size()];
        for (int i=0;i<profitList.length;i++){
            //移动均线
            float nowDwjz=fundDatas.get(i).getNetworth();
            mals[i%maVal]=nowDwjz;
            float avg=0;//平均值
            int m=0;//平均的天数
            float maSum=0;
            for(float d:mals){
                if(d!=0){
                    m++;
                    maSum+=d;
                }
            }
            if(m!=0)
                avg=maSum/m;

            //收益率集合
            if(i > maVal){
                float sectionMax= getMax(mals);//ma区间内的最大值
                float increase_rate = nowDwjz/avg;
                float decrease_rate = nowDwjz/sectionMax;

                if(increase_rate>buyThreshold){//超过买入比例
                    if(0==share){//且还未买入，则购买
                        share=cash/nowDwjz;
                        cash=0;

                        //新增一条交易记录
                        Trade trade = getTradeByIndex(fundDatas.get(i));
                        tradeList.add(trade);
                    }
                }else if(decrease_rate<sellThreshold){//低于卖点
                    if(0!=share){//且未卖，则出售
                        cash=nowDwjz*share;
                        share=0;

                        //修改交易记录的出售信息
                        Trade trade=tradeList.get(tradeList.size()-1);
                        trade.setSellDate(fundDatas.get(i).getFunddate());
                        trade.setSellClosePoint(nowDwjz);
                        trade.setRate(cash/initCash);

                        //计算交易统计相关数据
                        if(trade.getSellClosePoint()-trade.getBuyClosePoint()>0) {
                            totalWinRate +=(trade.getSellClosePoint()-trade.getBuyClosePoint())/trade.getBuyClosePoint();
                            winCount++;
                        }
                        else {
                            totalLossRate +=(trade.getSellClosePoint()-trade.getBuyClosePoint())/trade.getBuyClosePoint();
                            lossCount ++;
                        }
                    }
                }
            }
            float value;//目前的所有金额（买入的份额按照当日金额转换）
            if(share!=0)
                value=nowDwjz*share;
            else
                value=cash;
            float rate=value/initCash;//当前盈利率

            if(i-1<maVal){
                profitList[i]=nowDwjz;
            }else{
                profitList[i]=rate*init;
            }
        }

        float years = getYear(fundDatas);
        float indexIncomeTotal = (fundDatas.get(fundDatas.size()-1).getNetworth() - fundDatas.get(0).getNetworth()) / fundDatas.get(0).getNetworth();
        float indexIncomeAnnual = (float) Math.pow(1+indexIncomeTotal, 1/years) - 1;
        float trendIncomeTotal =  (profitList[profitList.length-1] - profitList[0]) / profitList[0];
        float trendIncomeAnnual = (float) Math.pow(1+trendIncomeTotal, 1/years) - 1;

        profitGlance.put("years",years);
        profitGlance.put("indexIncomeTotal",indexIncomeTotal);
        profitGlance.put("indexIncomeAnnual",indexIncomeAnnual);
        profitGlance.put("trendIncomeTotal",trendIncomeTotal);
        profitGlance.put("trendIncomeAnnual",trendIncomeAnnual);
        pandect.put("profitGlance",profitGlance);

        float avgWinRate = totalWinRate / winCount;
        float avgLossRate = totalLossRate / lossCount;

        tradingStatistics.put("winCount",winCount);
        tradingStatistics.put("lossCount",lossCount);
        tradingStatistics.put("avgWinRate",avgWinRate);
        tradingStatistics.put("avgLossRate",avgLossRate);
        pandect.put("tradingStatistics",tradingStatistics);

        pandect.put("tradeList",tradeList);

        return pandect;
    }

    public List<Trade> gridList(Map<String, Object> map,List<FundData> fundDatas){
        List<Trade> gridList=new ArrayList<>();//交易集合

        double gridWidth = Double.parseDouble(map.get("gridWidth").toString());
        double initAmount = Double.parseDouble(map.get("initAmount").toString());
        double initShare = Double.parseDouble(map.get("initShare").toString());
        double lastAmount= initAmount;//最后一次买入金额网格线
        double waveAmount=initAmount*gridWidth;//波动金额

        double highestDwjz=initAmount+waveAmount*(initShare+1);

        //建仓
        int build=0;//建仓的天数
        for(int i=0;i<fundDatas.size();i++){
            if(fundDatas.get(i).getNetworth()<=lastAmount){
                build=i+1;
                for(int j=0;j<initShare;j++){
                    Trade trade=getTradeByIndex(fundDatas.get(i));
                    gridList.add(trade);
                }
                break;
            }
        }

        //网格交易
        for(int i=build;i<fundDatas.size();i++){
            FundData fundData=fundDatas.get(i);
            if(fundData.getNetworth()<=lastAmount-waveAmount){//买
                Trade trade=getTradeByIndex(fundDatas.get(i));
                gridList.add(trade);
                lastAmount-=waveAmount;
            }else if(fundData.getNetworth()>=lastAmount+waveAmount && fundData.getNetworth()<highestDwjz){//卖(金额不能大于网格顶点)
                Trade trade=getUnsold(gridList);
                if(null!=trade) {
                    trade.setSellDate(fundData.getFunddate());
                    trade.setSellClosePoint(fundData.getNetworth());
                    trade.setRate(fundData.getNetworth() / trade.getBuyClosePoint() - 1);
                    lastAmount+=waveAmount;
                }
            }
        }

        SellAllGrid(gridList,fundDatas.get(fundDatas.size()-1));
        return gridList;
    }

    public Map<String,Object> gridStatistics(Map<String, Object> map, List<FundData> fundDatas, List<Trade> tradeList) {
        Map<String,Object> gridStatistics = new HashMap<>();
        float perGridAmount = Float.parseFloat(map.get("perGridAmount").toString());

        int gridCount=tradeList.size();//交易次数
        int completeCount=0;//完成次数
        float completeProfit=0;//完成的收益
        float profitCount=0;//总收益
        float completeRate=0;//完成的收益率
        float rateProfit=0;//总收益率

        float complCost=0;//完成的成本
        float costSum=0;//总成本
        for(Trade trade:tradeList){
            float share=perGridAmount/trade.getBuyClosePoint();//份额
            if(trade.getRate()>0){
                completeCount++;
                completeProfit+=(trade.getSellClosePoint()-trade.getBuyClosePoint())*share;
                complCost+=trade.getBuyClosePoint()*share;
            }
            profitCount+=(trade.getSellClosePoint()-trade.getBuyClosePoint())*share;
            costSum+=trade.getBuyClosePoint()*share;
        }

        if(complCost!=0)
            completeRate=completeProfit/complCost;
        if(costSum!=0)
            rateProfit=profitCount/costSum;

        float years=getYear(fundDatas);
        double rateAnnual=Math.pow(1+rateProfit, 1/years) - 1;

        gridStatistics.put("gridCount",gridCount);
        gridStatistics.put("completeCount",completeCount);
        gridStatistics.put("completeProfit",completeProfit);
        gridStatistics.put("profitCount",profitCount);
        gridStatistics.put("completeRate",completeRate);
        gridStatistics.put("rateProfit",rateProfit);
        gridStatistics.put("rateAnnual",rateAnnual);
        return gridStatistics;
    }


    public Map<String, Object> castSurely(Map<String, Object> map) {
        Map<String,Object> result=new HashMap<>();

        List<FundData> fundDatas=list(map);
        int cycle = Integer.parseInt(map.get("cycle").toString());
        float money = Float.parseFloat(map.get("money").toString());
        float profitRate = Float.parseFloat(map.get("profitRate").toString());

        int number=0;//定投次数
        float shareSum=0;//总份额
        for(int i=0;i<fundDatas.size();i+=cycle){
            shareSum += money/fundDatas.get(i).getNetworth();
            number++;

            float dwjz=fundDatas.get(i).getNetworth();
            float nowProfit=shareSum*dwjz-number*money;

            if(profitRate<=0 && nowProfit/number/money*100 >= profitRate){
                result.put("profit",nowProfit);
                result.put("sellDate",fundDatas.get(i).getFunddate());
                break;
            }
        }

        float nowDwjz=fundDatas.get(fundDatas.size()-1).getNetworth();

        if (!result.containsKey("profit")) {
            float profit = shareSum * nowDwjz - number * money;
            result.put("profit", profit);
        }
        if (!result.containsKey("sellDate"))
            result.put("sellDate","n/a");

        result.put("number",number);
        result.put("shareSum",shareSum);
        result.put("nowDwjz",nowDwjz);
        return result;
    }

    private Trade getTradeByIndex(FundData fundData){
        Trade trade=new Trade();
        trade.setBuyDate(fundData.getFunddate());
        trade.setBuyClosePoint(fundData.getNetworth());
        trade.setSellDate("n/a");
        trade.setSellClosePoint(0);
        return trade;
    }

    private float getMax(float[] floats){
        if(floats.length==0)
            return 0;

        float res=floats[0];
        for(float f:floats){
            if(res<f)
                res=f;
        }
        return res;
    }

    private float getYear(List<FundData> fundDatas) {
        try {
            String sDateStart = fundDatas.get(0).getFunddate();
            String sDateEnd = fundDatas.get(fundDatas.size()-1).getFunddate();

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Calendar cal = Calendar.getInstance();
            cal.setTime(sdf.parse(sDateStart));
            long time1 = cal.getTimeInMillis();
            cal.setTime(sdf.parse(sDateEnd));
            long time2 = cal.getTimeInMillis();
            long between_days=(time2-time1)/(1000*3600*24); // 天
            return between_days / 365F; // 默认按照365天/年
        } catch (ParseException e) {
            return 0;
        }
    }

    private Trade getUnsold(List<Trade> tradeList){
        Trade trade=null;
        for(int i=tradeList.size()-1;i>=0;i--){
            if(tradeList.get(i).getSellClosePoint()==0){
                trade=tradeList.get(i);
                break;
            }
        }
        return trade;
    }

    private void SellAllGrid(List<Trade> tradeList,FundData lastFund){
        for(Trade trade:tradeList){
            if(trade.getSellClosePoint()==0){
                trade.setSellClosePoint(lastFund.getNetworth());
                trade.setSellDate(lastFund.getFunddate());
                trade.setRate(lastFund.getNetworth() / trade.getBuyClosePoint() - 1);
            }
        }
    }

}
